package com.lord.otter.component.infrastructure.retry;

import com.lord.otter.component.infrastructure.facade.json.JSONFacade;
import com.lord.otter.component.infrastructure.retry.domain.HttpRetryTask;
import com.lord.otter.component.infrastructure.retry.domain.command.AddHttpRetryTaskCommand;
import com.lord.otter.component.infrastructure.retry.executor.HttpExecutorFacade;
import com.lord.otter.component.infrastructure.retry.repository.HttpRetryTaskRepository;
import com.lord.otter.component.infrastructure.retry.strategy.AbstractExceptionHandler;
import com.lord.otter.component.infrastructure.retry.strategy.RetryParameter;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * http请求重试客户端
 *
 * @author lord
 * 2025/8/8 17:28
 */
@Slf4j
@RequiredArgsConstructor
@Builder
public class HttpRetryClient {

    /**
     * 异常处理策略
     */
    private final AbstractExceptionHandler exceptionHandler;
    /**
     * 请求执行
     */
    private final HttpExecutorFacade executor;
    /**
     * HttpRetryTask仓储接口
     */
    private final HttpRetryTaskRepository taskRepository;
    /**
     * 业务组 - 用于区别业务
     */
    private final HttpRetryBusinessGroup businessGroup;
    /**
     * 业务主键
     */
    private final String businessId;
    /**
     * 请求地址
     */
    private final String url;
    /**
     * 成功回调的bean名称
     */
    private final String callbackBeanName;
    /**
     * 请求头
     */
    private final HttpHeaders httpHeaders;
    /**
     * 请求体
     */
    private final String requestBody;

    /**
     * 执行请求
     *
     * @param httpMethod 请求方式
     * @author lord
     * 2025/9/5 23:27
     */
    public void execute(HttpMethod httpMethod) {
        execute(String.class, httpMethod);
    }

    /**
     * 执行请求
     *
     * @param responseType 响应类型
     * @param httpMethod   请求方式
     * @author lord
     * 2025/8/16 23:54
     */
    public <T> ResponseEntity<T> execute(Class<T> responseType, HttpMethod httpMethod) {
        try {
            return executor.execute(url, httpMethod, httpHeaders, requestBody, responseType);
        } catch (Exception e) {
            e.printStackTrace();
            //log.error("执行请求异常", e);
            //确定重试策略，如果是不需要重试的，exceptionHandler.handle(e) 会往吧异常抛出去的
            RetryParameter parameter = exceptionHandler.handle(e);
            createRetryTask(e, httpMethod, parameter);
        }
        return null;
    }

    /**
     * 创建重试任务
     *
     * @param exception  什么异常导致需要重试
     * @param httpMethod 请求方式
     * @param parameter  重试的参数
     * @author lord
     * 2025/8/16 23:41
     */
    private void createRetryTask(Exception exception, HttpMethod httpMethod, RetryParameter parameter) {
        if (parameter == null) {
            log.warn("重试参数为空");
            return;
        }

        AddHttpRetryTaskCommand command = AddHttpRetryTaskCommand.builder()
                .url(this.url)
                .method(com.lord.otter.component.infrastructure.retry.constant.HttpMethod.valueOf(httpMethod.name()))
                .requestBody(this.requestBody)
                .headers(JSONFacade.toJSONString(this.httpHeaders))
                .firstExecError(getStackTrace(exception))
                .execModel(parameter.getExecModel())
                .delay(parameter.getDelay())
                .step(parameter.getStep())
                .pow(parameter.getPow())
                .businessGroup(this.businessGroup.getGroup())
                .businessId(this.businessId)
                .maxRetryNumber(parameter.getMaxRetryNumber())
                .callbackBeanName(this.callbackBeanName)
                .build();
        HttpRetryTask task = HttpRetryTask.create(command);
        taskRepository.add(task);
    }

    /**
     * 获取堆栈信息
     *
     * @param exception 异常信息
     * @author lord
     * 2025/8/17 00:09
     */
    private String getStackTrace(Exception exception) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        exception.printStackTrace(pw);
        return sw.toString();
    }
}
